﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using ScreenTester.Properties;
using System.Threading;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Drawing;

namespace ScreenTester
{
    public enum OperationType
    {
        MouseOperation,
        KeyboardOperation,
        Text,
        Separator,
        Application
    }


    public class Controller
    {
        private Controller()
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static Controller GetInstance()
        {
            return new Controller();
        }

        /// <summary>
        /// Execute Operation List
        /// </summary>
        /// <param name="operations"></param>
        public Bitmap[] ExecuteOperationList(ExecutiveOperation[] operations)
        {
            foreach (ExecutiveOperation oper in operations)
            {
                if (oper.OperType == OperationType.KeyboardOperation)
                {
                    KeyboardOperation keyOper = (KeyboardOperation)oper;
                    SetKeyboardEvent(keyOper.Keyboards);
                }
                else if (oper.OperType == OperationType.MouseOperation)
                {
                    MouseOperation mouseOper = (MouseOperation)oper;
                    SetMouseEvent(mouseOper.X, mouseOper.Y, mouseOper.MouseEvents);
                }
                else if (oper.OperType == OperationType.Text)
                {
                    TextInputOperation textOper = (TextInputOperation)oper;
                    SendKeys.SendWait(textOper.Text);
                }
                else if (oper.OperType == OperationType.Separator)
                {
                    SeparatorOperation sepOper = (SeparatorOperation)oper;
                    Thread.Sleep(sepOper.WaitingTime);
                }
                else if (oper.OperType == OperationType.Application)
                {
                    ApplicationOperation appOper = (ApplicationOperation)oper;
                    // find window handle of Notepad
                    IntPtr handle;
                    if (appOper.ApplicationName == null
                        || appOper.ApplicationTitle == null)
                    {
                        handle = GetDesktopWindow();
                    }
                    else
                    {
                        handle = FindWindow(appOper.ApplicationName, appOper.ApplicationTitle);
                    }
                    
                    if (!handle.Equals(IntPtr.Zero))
                    {
                        // activate Notepad window
                        SetForegroundWindow(handle);
                        MoveWindow(handle, 0, 0, appOper.Width, appOper.Height, true);
                    }
                }
            }

            // print screen
            return printScreen();
        }


        private Bitmap[] printScreen()
        {
            List<Bitmap> pics = new List<Bitmap>();
            foreach (Screen screen in Screen.AllScreens)
            {
                int x = screen.Bounds.X;
                int y = screen.Bounds.Y;
                int width = screen.Bounds.Width;
                int height = screen.Bounds.Height;
                // print screen 1
                Bitmap bmp = new Bitmap(width, height);
                Graphics g = Graphics.FromImage(bmp);
                g.CopyFromScreen(x, y, 0, 0, new Size(width, height));

                pics.Add(bmp);
            }

            return pics.ToArray<Bitmap>();
        }

        /// <summary>
        /// Set mouse event
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="dwFlags"></param>
        internal void SetMouseEvent(int x, int y, params MouseEventFlag[] dwFlags)
        {
            if (dwFlags.Length == 0)
            {
                return;
            }

            int flags = 0;
            foreach(MouseEventFlag f in dwFlags)
            {
                flags |= (int)f;
            }
            mouse_event(flags, x * 65536 / 1920, y * 65536 / 1080, 0, 0);
        }

        /// <summary>
        /// Set keyboard event
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="keyboards"></param>
        internal void SetKeyboardEvent(params Keyboard[] keys)
        {
            if(keys.Length == 0)
            {
                return;
            }
            if (keys.Length > 1)
            {
                foreach (Keyboard k in keys)
                {
                    keybd_event((byte)k, 0, (int)KeyboardEventFlag.KEY_DOWN, 0);  
                }
                for (int i = keys.Length - 1; i >= 0; i--)
                {
                    keybd_event((byte)keys[i], 0, (int)KeyboardEventFlag.KEY_UP, 0);  
                }

                return;
            }
            keybd_event((byte)keys[0], 0, (int)KeyboardEventFlag.KEY_DOWN, 0);
        }

        #region Win API
        /// <summary>
        /// 
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="X"></param>
        /// <param name="Y"></param>
        /// <param name="nWidth"></param>
        /// <param name="nHeight"></param>
        /// <param name="bRepaint"></param>
        /// <returns></returns>
        [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        private static extern IntPtr GetDesktopWindow();
        /// <summary>
        /// 
        /// </summary>
        /// <param name="lp1"></param>
        /// <param name="lp2"></param>
        /// <returns></returns>
        [DllImport("user32.dll", EntryPoint = "FindWindow")]
        private static extern IntPtr FindWindow(string lp1, string lp2);
        /// <summary>
        /// MSDN about 'SetCursorPos' http://msdn.microsoft.com/en-us/library/aa928050.aspx
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        private static extern int SetCursorPos(int x, int y);

        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/aa932376.aspx
        /// </summary>
        /// <param name="dwFlags"></param>
        /// <param name="dx"></param>
        /// <param name="dy"></param>
        /// <param name="cButtons"></param>
        /// <param name="dwExtraInfo"></param>
        [DllImport("user32.dll")]
        private static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);

        /// <summary>
        /// http://www.codeproject.com/KB/system/keyboard.aspx
        /// </summary>
        /// <param name="bVk"></param>
        /// <param name="bScan"></param>
        /// <param name="dwFlags"></param>
        /// <param name="dwExtraInfo"></param>
        [DllImport("user32.dll")]
        private static extern void keybd_event(
          byte bVk,
          byte bScan,
          byte dwFlags,
          byte dwExtraInfo);
        #endregion
    }
}
